using System;
using System.Globalization;
using System.Runtime.InteropServices;
using Microsoft.Win32;

namespace Framework
{
    public class NamespaceExtensionRegistrar
    {
        private readonly NamespaceExtensionAttribute _config;
        private readonly Type _type;

        public NamespaceExtensionRegistrar(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }
            if (!NamespaceExtensionAttribute.IsDefined(type))
            {
                throw new ArgumentOutOfRangeException("type", type, "must have NamespaceExtensionAttribute");
            }

            this._type = type;
            this._config = NamespaceExtensionAttribute.Get(type);
        }

        public void Register()
        {
            AddClsidAnnotations();
            AddToTarget();
            AddToApprovedShellExtensions();
        }

        public void Unregister()
        {
            RemoveClsidAnnotations();
            RemoveFromTarget();
            RemoveFromApprovedShellExtensions();
        }

        private void AddClsidAnnotations()
        {
            using (RegistryKey key = OpenClsid())
            {
                key.SetValue(null, Config.Name);

                if (Config.InfoTip != null)
                {
                    key.SetValue("InfoTip", Config.InfoTip);
                }

                using (RegistryKey subkey = key.CreateSubKey("ShellFolder"))
                {
                    subkey.SetValue("Attributes", BitConverter.GetBytes((int)Config.Attributes));
                }

                if (HasIcon)
                {
                    using (RegistryKey subkey = key.CreateSubKey("DefaultIcon"))
                    {
                        subkey.SetValue(null, IconString);
                    }
                }
            }
        }

        private void AddToApprovedShellExtensions()
        {
            using (RegistryKey key = OpenApprovedShellExtensions())
            {
                key.SetValue(TypeGuid, string.Format(CultureInfo.InvariantCulture, "{0}", Config.Name));
            }
        }

        private void AddToTarget()
        {
            if (!HasTarget)
            {
                return;
            }

            using (RegistryKey key = OpenTarget())
            {
                using (RegistryKey subkey = key.CreateSubKey(TypeGuid))
                {
                    subkey.SetValue(null, Config.Name);
                }
            }
        }

        private NamespaceExtensionAttribute Config
        {
            get { return this._config; }
        }

        private bool HasIcon
        {
            get { return Config.IconIndex >= 0 || Config.IconString != null; }
        }

        private bool HasTarget
        {
            get { return Config.Target != Targets.None; }
        }

        private string IconString
        {
            get
            {
                return (Config.IconIndex >= 0)
                           ? string.Format(CultureInfo.InvariantCulture, "{0},{1}", this._type.Assembly.CodeBase,
                                           Config.IconIndex)
                           : Config.IconString;
            }
        }

        private RegistryKey OpenApprovedShellExtensions()
        {
            return
                Registry.LocalMachine.OpenSubKey(
                    @"Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved", true);
        }

        private RegistryKey OpenClsid()
        {
            return Registry.ClassesRoot.CreateSubKey(string.Format(CultureInfo.InvariantCulture, @"CLSID\{0}", TypeGuid));
        }

        private RegistryKey OpenTarget()
        {
            if (Config.Target == Targets.None)
            {
                throw new InvalidOperationException("No target namespace");
            }

            return
                Registry.LocalMachine.OpenSubKey(
                    string.Format(CultureInfo.InvariantCulture,
                                  @"Software\Microsoft\Windows\CurrentVersion\Explorer\{0}\Namespace",
                                  TargetNameAttribute.GetName(Config.Target)), true);
        }

        private void RemoveClsidAnnotations()
        {
            using (RegistryKey key = OpenClsid())
            {
                key.DeleteValue("InfoTip", false);
                key.DeleteSubKey("ShellFolder", false);
                key.DeleteSubKey("DefaultIcon", false);
            }
        }

        private void RemoveFromApprovedShellExtensions()
        {
            using (RegistryKey key = OpenApprovedShellExtensions())
            {
                key.DeleteValue(TypeGuid, false);
            }
        }

        private void RemoveFromTarget()
        {
            if (!HasTarget)
            {
                return;
            }

            using (RegistryKey key = OpenTarget())
            {
                key.DeleteSubKey(TypeGuid);
            }
        }

        private string TypeGuid
        {
            get
            {
                var g = (GuidAttribute)Attribute.GetCustomAttribute(this._type, typeof (GuidAttribute));
                return string.Format("{{{0}}}", g.Value);
            }
        }
    } ;
}
